package mobile.TheOldRepublic.web;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import mobile.TheOldRepublic.util.Constants;

import org.htmlcleaner.CleanerProperties;
import org.htmlcleaner.HtmlCleaner;
import org.htmlcleaner.PrettyHtmlSerializer;
import org.htmlcleaner.TagNode;
import org.htmlcleaner.Utils;

import com.explodingzombie.util.GeneralUtils;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.Display;
import android.view.KeyEvent;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;

public class WebViewClientWrapper extends WebViewClient {
	
	private Context context = null;
	int width = Constants.WEB_DEFAULT_WIDTH;
	private String contextRoot = null;
	
	// Debug
	private String lastHtml = null;
	
	@Override
	public boolean shouldOverrideUrlLoading(WebView view, String urlString){
		if(GeneralUtils.isEmpty(urlString)) return false;
		
		boolean override = false;
		for(String od : Constants.OVERRIDE_DOMAINS)
			if(urlString.toLowerCase().contains(od)){
				override = true;
				break;
			}
		
		if(override){
			BufferedReader inReader = null;
			try{
				CleanerProperties props = new CleanerProperties();
				props.setOmitComments(true);
				props.setAllowHtmlInsideAttributes(true);
				
				URL url = new URL(urlString);
				contextRoot = url.getProtocol()+"://"+url.getHost()+"/";
				URLConnection conn = url.openConnection();
					Log.d(this.getClass().getSimpleName(), "Loading Intercepted");
					
					if(context != null && context instanceof Activity){
						Display dis = ((Activity)context).getWindowManager().getDefaultDisplay();
						width = dis.getWidth();
					}
					
					inReader = new BufferedReader(new InputStreamReader(conn.getInputStream()));

					HtmlCleaner cleaner = new HtmlCleaner(props);
					TagNode source = cleaner.clean(inReader);
					inReader.close();

					TagNode body = source.findElementByName("body", false);

					// Modifying the page via jQuery turned out to be too slow.  So we're instead modifying the
					// html before it's rendered. 20110406LP
					Log.d(this.getClass().getSimpleName(), "Beginning HTML Parsing.");
					parseChildElements(body);
					TagNode js = new TagNode("script");
					js.setAttribute("language", "text/javascript");
					cleaner.setInnerHtml(js, "$(document).ready(function(){ $('.newsText img').each(function(){ $(this).css('width','50%'); $(this).css('height','50%');}); });");
					body.addChild(js);
					Log.d(this.getClass().getSimpleName(), "Finished HTML Parsing.");
					
					// Append out injected javascript right before the closing HEAD tag. LP
					/*List<Element> head = source.getAllElements(HTMLElementName.HEAD);
					EndTag end;
					if(!head.isEmpty()){
						// There should only ever be one HEAD element. LP
						end = head.get(0).getEndTag();
					}else{
						// If head is missing use body. LP
						List<Element> body = source.getAllElements(HTMLElementName.BODY);
						// If body is also missing then something is wrong with the page. LP
						if(body.isEmpty()) return false;
						end = body.get(0).getEndTag();
					}
					outHTML.replace(end, html.toString());*/
					PrettyHtmlSerializer serializer = new PrettyHtmlSerializer(props);
					StringWriter outHtml = new StringWriter();
					source.serialize(serializer, outHtml);
					lastHtml = outHtml.toString();
					
					// Debugging
					/*try{
						System.out.println(outHtml.toString());
						Intent i = new Intent(Intent.ACTION_SEND);
						i.setPackage("com.google.android.gm");
						i.setType("text/html");
						i.putExtra(Intent.EXTRA_EMAIL, new String[]{});
						i.putExtra(Intent.EXTRA_SUBJECT, "ITTR Test HTML");
						i.putExtra(Intent.EXTRA_TEXT, outHtml.toString());
//						context.startActivity(Intent.createChooser(i, "Send debug report"));
						context.startActivity(i);
					}catch(Exception e){e.printStackTrace();}*/
					
					view.loadDataWithBaseURL(urlString, outHtml.toString(), 
							null, Constants.HTML_ENCODING, null);
					return true;
				
			}catch(IOException ioe){
				Log.e(this.getClass().getSimpleName(), "An unexpected error occured.");
				ioe.printStackTrace();
			}finally{
				if(inReader != null)
					try{ inReader.close(); }catch(Exception e){}
			}
		}
		
		Log.d(this.getClass().getSimpleName(), "Loading normal");
		return false;
	}
	
	/**
	 * Recursive function for modifying HTML element.  This function also decides 
	 * which elements should be kept and which are removed.
	 * @param e The current Element to parse.
	 * @param out The OutputDocument to write changes to.
	 * @return short value defining if the current Element will be kept. -1 if false, 1 if true and 
	 * 0 if not applicable. (0 is mainly for tags we aren't processing.)
	 * A value of 1 will be propagated to all parent Elements.
	 */
	private short parseChildElements(TagNode e)
	{		
		// We don't want to have to process _everything_ like a and img tags unless we want to. LP
		String tagType = e.getName();
		// Remove comments. LP
		if(tagType.equals("!--")) return -1;
		
		short keep = (tagType.equals("div") || tagType.equals("span")) ? (short)-1 : (short)0;
//		System.out.println("Target tag type: "+tagType+" default value "+keep);
		
		// Extract Attributes in case we want to replace them. LP
		Map<String, String> attrMap = e.getAttributes();
		boolean replaceAttrs = false;
		
		String id = (attrMap.containsKey("id")) ? attrMap.get("id").toLowerCase() : "";
		String styleClass = (attrMap.containsKey("class")) ? attrMap.get("class").toLowerCase() : "";
		String style = attrMap.containsKey("style") ? attrMap.get("style").toLowerCase() : "";
		
		// Process tag id's
		if("header".equals(id)){
			keep = 1;
		}else if("flashheader".equals(id)){
			style += " height: 0px; ";
			attrMap.put("style", style);
			
			replaceAttrs = true;
			keep = -1;
		}else if("navbar".equals(id)){
			keep = 1;
		}else if ("poll".equals(id)){
			keep = 1;
		}else if ("headercontent".equals(id)){
			style += " width: 100%; ";
			attrMap.put("style", style);
			
			replaceAttrs = true;
			keep = 1;
		}else if ("maincontentfull".equals(id)){
			style += " margin: 0px; width: 100%; ";
			attrMap.put("style", style);
			
			replaceAttrs = true;
			keep = 1;
		}else if ("mainbody".equals(id)){
			style += " margin: 0px; width: 100%; background-image: none;";
			attrMap.put("style", style);
			
			replaceAttrs = true;
			keep = 1;
		}else if("page".equals(id)){
			if(style.contains("min-width")){
				int start = style.indexOf("min-width");
				int end = style.indexOf(";", start);
				style = style.substring(0, start-1)+style.substring(end+1);
			}
			style += " background: none; min-height: 100px; height: 100%; ";
			attrMap.put("style", style);
			
			replaceAttrs = true;
			keep = 1;
		}else if("nice-menu-1".equals(id)){
			style += " left: 0px; top: 20px; float: none;";
			attrMap.put("style", style);
			
			replaceAttrs = true;
			keep = 1;
			
		}
		// I'm gonna try to remove registration to see how I like the layout. 20110414LP
		else if("registration".equals(id)){
		style += " position: relative; padding-top: 7px; display: block;";//float: left; ";
		attrMap.put("style", style);
		
		replaceAttrs = true;
		keep = 1;
		}/*else if("registration".equals(id)){
			style += " display: none;";
			attrMap.put("style", style);
			
			replaceAttrs = true;
			keep = -1;
		}*/else if("loginattop".equals(id)){
			keep = -2;
		}else if("logininfo".equals(id)){
			keep = -2;
		}
		
		else if("language-selector".equals(id)){
			style += " position: relative; display: block; float: none;";
			attrMap.put("style", style);
			
			replaceAttrs = true;
			keep = 1;
		}else if("sidecontent".equals(id)){
			style += " display: none;";
			attrMap.put("style", style);
			
			replaceAttrs = true;
			keep = -1;
		}else if("subheadwrap".equals(id)){
			style += " height: 0px; ";
			attrMap.put("style", style);
			
			replaceAttrs = true;
			keep = -1;
		}else if("interiorcontenttop".equals(id)){
			style += " height: 35px; background-image: none; width: 100%";
			attrMap.put("style", style);
			
			replaceAttrs = true;
			keep = -1;
		}/*else if("gamesystemsheader".equals(id)){
			keep = 1;
		}*/else if("gamesystemsbreadcrumb".equals(id)){
			keep = 1;
		}else if("gamesystemscontent".equals(id)){
			keep = 1;
		}else if("gamesystemsmainbox".equals(id)){
			keep = 1;
		}else if("gamesystemsmain".equals(id)){
			keep = 1;
		}else if("page".equals(id)){
			style += " min-width: 0px; ";
			attrMap.put("style", style);
			
			replaceAttrs = true;
			keep = 1;
		}else if("classintrotext".equals(id)){
			keep = 1;
		}else if("classintropad".equals(id)){
			keep = 1;
		}else if("classrepublic".equals(id)){
			keep = 1;
		}else if("classempire".equals(id)){
			keep = 1;
		}else if("classpanels".equals(id)){
			keep = 1;
		}else if(id.contains("gameinfo")){
			keep = 1;
		}

		
		// Process tag classes
		// We always use contains here because multiple classes can be defined. LP
		// I'm gonna try to remove registration to see how I like the layout. 20110414LP
		/*if(styleClass.contains("registration")){
			style += " max-width: "+width+"px; display: inline; left: 0px;";
			attrMap.put("style", style);
			replaceAttrs = true;
			keep = 1;
		}*/
		if(styleClass.contains("registration")){
			style += " display: none;";
			attrMap.put("style", style);
			replaceAttrs = true;
			keep = -2;
		}
		if(styleClass.contains("forumsub")){
			style += " margin: 0px;";
			attrMap.put("style", style);
			
//			if(styleClass.matches("odd.+forumsub")){
//				styleClass.replace("odd  forumsub", "even forumSub");
				// TODO Get Regex working.
			Log.d(this.getClass().getSimpleName(), "|"+styleClass+"|");
//				styleClass.replaceAll("odd.+forumsub", "even forumSub");
				styleClass = "even forumSub";
				attrMap.put("class", styleClass);
//			}
			replaceAttrs = true;
			keep = 1;
		}
		if(styleClass.contains("maincontentfulltop")){
			style += " background-image: none; width: 100%;";
			attrMap.put("style", style);
			replaceAttrs = true;
			keep = 1;
		}
		if(styleClass.contains("maincontentfulltitle")){
			style += " max-width: 100%;";
			attrMap.put("style", style);
			replaceAttrs = true;
			keep = 1;
		}
		if(styleClass.contains("maincontentbody")){
			style += " width: 100%; background-image: none;";
			attrMap.put("style", style);
			replaceAttrs = true;
			keep = 1;
		}
		// I'm gonna try removing comments to see how I like it. 20110414LP
		/*if(styleClass.contains("maincontentcommentheader")){
			style += " background-image: none; width: 100%;";
			attrMap.put("style", style);
			replaceAttrs = true;
			keep = 1;
		}*/
		if(styleClass.contains("maincontentcommentheader")){
			style += " display: none;";
			attrMap.put("style", style);
			replaceAttrs = true;
			keep = -1;
		}
		if(styleClass.contains("contentpadding")){
			keep = 1;
		}
		if(styleClass.contains("localization")){
			keep = 1;
		}
		if(styleClass.contains("navbar")){
			keep = 1;
		}
		if(styleClass.contains("thumbbackground")){
			keep = 1;
		}
		if(styleClass.contains("resulttitle")){
			keep = 1;
		}
		if(styleClass.contains("resulttext")){
			keep = 1;
		}
		if(styleClass.contains("resultsubtext")){
			keep = 1;
		}
		if(styleClass.contains("thumbbackground")){
			keep = 1;
		}
		// I'm gonna try removing comments to see how I like it. 20110414LP
		/*if(styleClass.contains("comment")){
			attrMap.put("width", "100%");
			replaceAttrs = true;
			keep = 1;
		}*/
		if(styleClass.contains("comment")){
			keep = -2;
		}
		if(styleClass.contains("newstext")){
			keep = 1;
		}
		if(styleClass.contains("contentpadding")){
			attrMap.put("margin", "0");
			replaceAttrs = true;
			keep = 1;
		}
		if(styleClass.contains("resultcategory")){
			keep = 1;
		}
		if(styleClass.contains("resultsubtext")){
			keep = 1;
		}
		if(styleClass.contains("interiortitle")){
			style += " position: relative; height: 77px; width: 100%;";
			attrMap.put("style", style);
			
			replaceAttrs = true;
			keep = 1;
		}
		if(styleClass.contains("skills")){
			keep = 1;
		}
		if(styleClass.contains("shared")){
			keep = 1;
		}
		if(styleClass.contains("sharedskills")){
			style += " float: none;";
			attrMap.put("style", style);
			
			replaceAttrs = true;
			keep = 1;
		}
		if(styleClass.contains("screenshots")){
			style += " float: none; width: 100%;";
			attrMap.put("style", style);
			
			replaceAttrs = true;
			keep = 1;
		}
		if(styleClass.contains("articletags")){
			style += " background-image: none; width: 100%;";
			attrMap.put("style", style);
			
			replaceAttrs = true;
			keep = 1;
		}
		// I'm gonna try to remove registration to see how I like the layout. 20110414LP
		if(styleClass.contains("registration")){
			/*if(style.contains("display")){
				int ds = style.indexOf("display");
				int de = style.indexOf(";", ds);
				style = style.substring(0, ds-1)+style.substring(de+1);
			}
			style += " position: relative; padding-top: 7px; display: block;";//float: left; ";
			attrMap.put("style", style);
			
			replaceAttrs = true;
			keep = 1;*/
			keep = -2;
		}
		if(styleClass.contains("logininvalidpassword") || styleClass.contains("loginforgotpassword")){
			keep = 1;
		}
		
		if(!replaceAttrs){
			try{
				String width = attrMap.containsKey("width") ? attrMap.get("width").toLowerCase() : "";
				if(Integer.parseInt(width) > this.width){
					attrMap.put("width", "100%");
					replaceAttrs = true;
				}
			}catch(Exception ex){}
		}
		
		// We probably want to keep all forms. LP
		if(tagType.equals("form")){
			Log.d(this.getClass().getSimpleName(), "Default to 1");
			keep = 1;
		}
		if(tagType.equals("a")){
			String href = attrMap.containsKey("href") ? attrMap.get("href").toLowerCase() : "";
			if(href.length() > 0){
				e.setAttribute("href", Utils.fullUrl(contextRoot, href));
			}
		}
		if(tagType.equals("img")){
			String src = attrMap.containsKey("src") ? attrMap.get("src").toLowerCase() : "";
			if(src.length() > 0){
				src = Utils.fullUrl(contextRoot, src);
				e.setAttribute("src", src);
			}
		}
			
		// If we can't identify the tag then we may not want to remove it.
		// Let other Elements decide it's fate. LP
		// TODO Verify this is true.
		/*if(keep == -1 && "".equals(id) && "".equals(styleClass)){
			System.out.println("Default to 0");
			keep = 0;
		}*/
		
		
		if(replaceAttrs){
			// TODO May be unnecessary. Need to test.
			for(String key : attrMap.keySet()){
				e.setAttribute(key, attrMap.get(key));
			}
		}
		
		// Now lets actually remove elements we don't want from the html. LP
		if(keep != -2 && e.hasChildren()){
			List children = e.getChildren();
			for(Iterator i = children.iterator(); i.hasNext();){
				Object o = i.next();
				if(!(o instanceof TagNode)) continue;
				TagNode child = (TagNode)o;
					
				short keepChild = 0; 
				try{
					keepChild = parseChildElements(child);
				}catch(StackOverflowError soe){
					soe.printStackTrace();
					Log.e(this.getClass().getSimpleName(), "Stack Overflow parsing HTML");
				}
				if(keepChild <= -1){
					i.remove();
				}else if(keepChild == 1){
					keep = 1;
				}// If keepChild == 0 then do nothing. LP
			}
		}
		
		if(keep <= -1) Log.d(this.getClass().getSimpleName(), 
				"Removing "+tagType+" id:"+id+" class:"+styleClass);
		
		return keep;
	}

	@Override
	public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
		Log.w(this.getClass().getSimpleName(), "Error loading: "+failingUrl+" "+errorCode+":"+description);
		if(context != null)
		   Toast.makeText(context, "An error occured: " + description, Toast.LENGTH_LONG).show();
	}
	
	@Override
	public void onUnhandledKeyEvent(WebView view, KeyEvent event){
		if(event.getKeyCode() == KeyEvent.KEYCODE_BACK){
			Log.d(this.getClass().getSimpleName(), "Unhandled back press");
		}
		super.onUnhandledKeyEvent(view, event);
	}
	
	@Override
	public void onLoadResource(WebView view, String url){
		Log.d(this.getClass().getSimpleName(), "Reviewing resource: "+url);
		// Replace images we don't need or are too large for mobile layouts. LP
		if(url.matches("files.*avatars.*[gif|jpg|png]"))
			return;			

		Log.d(this.getClass().getSimpleName(), "Loading resource: "+url);
		super.onLoadResource(view, url);
	}
	
	public void setContext(Context context){
		this.context = context;
	}
	public Context getContext(){
		return this.context;
	}

	public String getLastHtml() {
		return lastHtml;
	}
	public void setLastHtml(String lastHtml) {
		this.lastHtml = lastHtml;
	}

	public int getWidth() {
		return width;
	}
	public void setWidth(int width) {
		this.width = width;
	}
	
}